package triTree;

import transaction.*;
import triStorage.*;
import triBasic.*;
import triDataStruct.*;
import triInstance.*;

import java.util.*;

//? is getTriPath essential?
public class WritableTriNode extends BufferObject {
	
	//the node itself
	private NodeSnapshot value;
	
	//father and children's information
	private NodeSnapshot father;

	//children are created everytime the node is loaded...
	//never reuse the nodesnapshot
	private Vector<NodeSnapshot> children;
	
	private Vector<NodeInfo> ancestors;
	
	//write changes
	private IWriter writer;
	
	//for use of NodeManager
	public int bufferIndex;
	
	private static NodeSnapshot nil = new NodeSnapshot(-1, "", -1, -1);
	
	public WritableTriNode(int index) {
		super();
		bufferIndex = index;
		value = new NodeSnapshot();
		writer = null;

		father = new NodeSnapshot();
		children = new Vector<NodeSnapshot> ();
		ancestors = new Vector<NodeInfo>();
		
		//lock = new RWLock();
	}
	
	public WritableTriNode(NodeSnapshot value, WritableTriNode faNode, IWriter writer) {
		super();
		this.value = value;
		this.writer = writer;
		children = new Vector<NodeSnapshot>();
		ancestors = new Vector<NodeInfo> ();
		
		if (faNode == null) {
			this.father = nil;
		}
		else {
			this.father = faNode.getNodeSnapshot();
			
			for (int i = 0; i < faNode.getAncestorCount(); ++i)
				ancestors.add(faNode.getNthAncestor(i));
			ancestors.add(this.father.copy());
		}
			
		//lock = new RWLock();
	}
	
	public INode copy(Transaction tx) {
		//only for query
		lock.lockRead(tx);
		try {
			INode node = new ReadableTriNode(this, tx);
			return node;
		}
		finally {
			lock.unlockRead(tx);
		}
	}
	
	public int getNodeId() {
		return this.value.getNodeId();
	}
	
	public String getTagName() {
		return this.value.getTagName();
	}
	
	public int getChildCount(Transaction tx) {
		lock.lockRead(tx);
		try {
			int value = this.children.size();
			return value;
		}
		finally {
			lock.unlockRead(tx);
		}
	}
	
	public NodeSnapshot getNthChild(int ith, Transaction tx) {
		lock.lockRead(tx);
		try {
			if (ith >= this.children.size())
				return null;
			NodeSnapshot snapshot = this.children.get(ith);
			return snapshot;
		}
		finally {
			lock.unlockRead(tx);
		}
	}
	
	public NodeSnapshot findChild(String tagName, Transaction tx) {
		lock.lockRead(tx);
		try {
			for (int i = 0; i< children.size(); ++i)
				if (children.get(i).getTagName().equals(tagName)) 
					return children.get(i);
			return null;
		}
		finally {
			lock.unlockRead(tx);
		}
		
	}
	
	public int getAncestorCount() {
		synchronized (ancestors) {
			return this.ancestors.size();
		}
	}
	
	public NodeInfo getNthAncestor(int ith) {
		synchronized (ancestors) {
			return this.ancestors.get(ith);
		}
	}
	
	public boolean isNode(NodeSnapshot snapshot) {
		return snapshot.getNodeId() == this.value.getNodeId();
	}
	
	@Override
	public int getKey() {
		return this.value.getNodeId();
	}
	
	public NodeSnapshot getNodeSnapshot() {
		return this.value;
	}
	
	public NodeSnapshot getFather() {
		return this.father;
	}

	public void addChild(NodeSnapshot child, Transaction tx) {
		if (writer != null) {
			
			lock.lockWrite(tx);
			try {
				this.children.add(child);
				//System.out.println("add... then flush....");
				child.flush(writer, tx);
			}
			finally {
				lock.unlockWrite(tx);
			}			
		}
	}
	
	public void load(IReader reader, Transaction tx) {
		this.value.load(reader);
		
		this.father.load(reader);
		ancestors.clear();
		int cnt = reader.readInt();
		for (int i = 0; i < cnt; ++i)
			ancestors.add(new NodeInfo(reader.readInt(), reader.readString()));
		
		children.clear();
		NodeSnapshot child;
		while (true) {
			child = new NodeSnapshot();
			if (child.load(reader))
				children.add(child);
			else 
				break;
		}
		
		
		this.writer = reader.toWriter(tx);
	}	
	
	//only flush
	public void flush(Transaction tx) {
		if (writer == null)
			return;
		//System.out.println("flush node " + this.value.getNodeId());
		this.value.flush(writer, tx);
		
		//System.out.println("flush father");
		this.father.flush(writer, tx);
		
		//System.out.println("flush ancestor");
		int cnt = ancestors.size();
		writer.appendInt(cnt, tx);
		NodeInfo info;
		for (int i = 0; i < cnt; ++i) {
			info = ancestors.get(i);
			writer.appendInt(info.getNodeId(), tx);
			writer.appendString(info.getTagName(), tx);
		}
			
		
		int childCount = this.children.size();
		for (int i = 0; i < childCount; ++i) 
			children.get(i).flush(writer, tx);
		
		//System.out.println("flush ends");
	}
	
	public void dispose() {
		if (this.writer != null)
			this.writer.dispose();
	}
}



